package com.lql.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.utils.BeanUtils;
import com.hmall.common.utils.CollUtils;
import com.lql.api.dto.ItemDTO;
import com.lql.search.domain.po.Item;
import com.lql.search.domain.po.ItemDoc;
import com.lql.search.domain.query.ItemPageQuery;
import com.lql.search.domain.vo.CategoryAndBrand;
import com.lql.search.mapper.SearchMapper;
import com.lql.search.service.ISearchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品表 服务实现类
 * </p>
 *
 * @author 虎哥
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SearchServiceImpl extends ServiceImpl<SearchMapper, Item> implements ISearchService {

    private final RestHighLevelClient client = new RestHighLevelClient(
            RestClient.builder(HttpHost.create("http://192.168.44.128:9200"))
    );

    @Override
    public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
        return BeanUtils.copyList(listByIds(ids), ItemDTO.class);
    }

    @Override
    public PageDTO<ItemDTO> searchEs(ItemPageQuery query) throws IOException {
        log.info("searchEs: {}", query);
        // 1.创建request
        SearchRequest request = new SearchRequest("items");
        // 2.设置参数
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (query.getKey() != null && !query.getKey().isEmpty()) {
            boolQuery.must(QueryBuilders.matchQuery("name", query.getKey()));
        } else {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }

        // 添加分类过滤条件（带非空判断）
        if (query.getCategory() != null && !query.getCategory().isEmpty()) {
            boolQuery.filter(QueryBuilders.termQuery("category.keyword", query.getCategory()));
        }

        // 添加品牌过滤条件（带非空判断）
        if (query.getBrand() != null && !query.getBrand().isEmpty()) {
            boolQuery.filter(QueryBuilders.termQuery("brand.keyword", query.getBrand()));
        }

        // 添加价格范围过滤（带默认值处理）
        boolQuery.filter(QueryBuilders.rangeQuery("price")
                .gte(query.getMinPrice() != null ? query.getMinPrice() : 0)
                .lte(query.getMaxPrice() != null ? query.getMaxPrice() : Integer.MAX_VALUE)
        );

        SearchSourceBuilder source = request.source().query(boolQuery);
        // 分页参数处理
        int pageNo = query.getPageNo() != null && query.getPageNo() > 0 ? query.getPageNo() : 1;
        int pageSize = query.getPageSize() != null && query.getPageSize() > 0 ? Math.min(query.getPageSize(), 100) : 10;
        source.from((pageNo - 1) * pageSize);
        source.size(pageSize);

        // 排序参数处理
        if (query.getSortBy() != null && !query.getSortBy().isEmpty()) {
            SortOrder sortOrder = query.getIsAsc() != null && query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC;
            source.sort(query.getSortBy(), sortOrder);
        } else {
            // 默认按价格降序排序
            source.sort("price", SortOrder.DESC);
        }

        // 高亮参数处理（仅在搜索关键词存在时启用）
        if (query.getKey() != null && !query.getKey().trim().isEmpty()) {
            source.highlighter(
                    SearchSourceBuilder.highlight()
                            .field("name")
                            .preTags("<font color='red'>")
                            .postTags("</font>")
            );
        }

        // 3.执行请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.处理结果
        PageDTO<ItemDTO> pageDTO = new PageDTO<>();
        SearchHits searchHits = response.getHits();
        // 4.1 设置总记录数
        long total = searchHits.getTotalHits().value;
        pageDTO.setTotal(total);

        // 4.2 设置总页数
        long pages = total % query.getPageSize() == 0 ? total / query.getPageSize() : total / query.getPageSize() + 1;
        pageDTO.setPages(pages);

        // 4.3 获取数据
        SearchHit[] hits = searchHits.getHits();
        // 4.4 设置返回结果的数组
        List<ItemDTO> list = new ArrayList<>();
        for (SearchHit hit : hits) {
            // 4.4.1 获取Source数据
            String json = hit.getSourceAsString();
            // 4.4.2 转换为ItemDoc对象
            ItemDoc itemDoc = JSONUtil.toBean(json, ItemDoc.class);
            // 4.4.3 转换为ItemDTO对象
            ItemDTO itemDTO = BeanUtil.copyProperties(itemDoc, ItemDTO.class);

            // 4.4.4 获取高亮结果
            Map<String, HighlightField> hfs = hit.getHighlightFields();
            if (CollUtils.isNotEmpty(hfs)) {
                // 4.4.5 获取name高亮结果
                HighlightField hf = hfs.get("name");
                if (hf != null) {
                    // 4.4.6 获取高亮结果
                    String hfv = hf.getFragments()[0].toString();
                    // 4.4.7 设置高亮结果
                    itemDTO.setName(hfv);
                }
            }
            list.add(itemDTO);
        }
        // 4.5 设置返回结果
        pageDTO.setList(list);
        return pageDTO;
    }

    @Override
    public CategoryAndBrand filters(ItemPageQuery query) throws IOException {
        // 1.创建request
        SearchRequest request = new SearchRequest("items");
        // 2.设置参数
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (query.getKey() != null && !query.getKey().isEmpty()) {
            boolQuery.must(QueryBuilders.matchQuery("name", query.getKey()));
        } else {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        // 添加分类过滤条件（带非空判断）
        if (query.getCategory() != null && !query.getCategory().isEmpty()) {
            boolQuery.filter(QueryBuilders.termQuery("category.keyword", query.getCategory()));
        }

        // 添加品牌过滤条件（带非空判断）
        if (query.getBrand() != null && !query.getBrand().isEmpty()) {
            boolQuery.filter(QueryBuilders.termQuery("brand.keyword", query.getBrand()));
        }
        // 添加价格范围过滤（带默认值处理）
        boolQuery.filter(QueryBuilders.rangeQuery("price")
                .gte(query.getMinPrice() != null ? query.getMinPrice() : 0)
                .lte(query.getMaxPrice() != null ? query.getMaxPrice() : Integer.MAX_VALUE)
        );
        request.source()
                .query(boolQuery)
                .size(0)
                .aggregation(AggregationBuilders.terms("category_agg").field("category.keyword"))
                .aggregation(AggregationBuilders.terms("brand_agg").field("brand.keyword"));

        // 3.执行请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.处理结果
        Aggregations aggregations = response.getAggregations();
        // 4.1 获取分类聚合结果
        Terms categoryAgg = aggregations.get("category_agg");
        // 4.2 获取品牌聚合结果
        Terms brandAgg = aggregations.get("brand_agg");

        // 4.3 获取分类桶
        List<String> category = categoryAgg.getBuckets().stream()
                .map(bucket -> bucket.getKeyAsString())
                .collect(Collectors.toList());

        // 4.4 获取品牌桶
        List<String> brand = brandAgg.getBuckets().stream()
                .map(bucket -> bucket.getKeyAsString())
                .collect(Collectors.toList());

        return new CategoryAndBrand(category, brand);
    }
}
